package org.jing.ext.compress.archieve;

import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry;
import org.apache.commons.compress.archivers.sevenz.SevenZFile;
import org.apache.commons.compress.utils.MultiReadOnlySeekableByteChannel;
import org.jing.core.lang.JingException;
import org.jing.core.logger.JingLogger;
import org.jing.core.util.FileUtil;
import org.jing.core.util.StringUtil;
import org.jing.ext.compress.BaseDecompress;

import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * Description: 解压 <br>
 * 依赖:
 *
 * @author bks <br>
 * @since 2023-02-22 <br>
 */
public class DecompressFor7z extends BaseDecompress {
    private static final JingLogger LOGGER = JingLogger.getLogger(DecompressFor7z.class);

    public DecompressFor7z() {
        super();
    }

    public DecompressFor7z(BaseDecompress decompress) {
        super(decompress);
    }

    @Override
    public List<File> decompress(File srcFile, String outputDirectory) {
        try (
            SevenZFile sevenZFile = StringUtil.isEmpty(password)
                ? new SevenZFile(srcFile)
                : new SevenZFile(srcFile, password.toCharArray())
        ) {
            List<File> resultFiles = operate(sevenZFile, outputDirectory);
            if (deleteSrc) {
                FileUtil.deleteFile(srcFile);
            }
            return resultFiles;
        }
        catch (JingException e) {
            throw e;
        }
        catch (Throwable t) {
            throw new JingException(t);
        }
    }

    /**
     * Description: 解压7z算法的多个压缩包. <br>
     *
     * @param srcFiles 必须以正确的顺序排列的源文件列表 <br>
     * @param outputDirectory 输出目录 <br>
     * @return 解压后的文件列表 <br>
     */
    @Override
    public List<File> decompressMultiple(File[] srcFiles, String outputDirectory) {
        try (
            SevenZFile sevenZFile = StringUtil.isEmpty(password)
                ? new SevenZFile(MultiReadOnlySeekableByteChannel.forFiles(srcFiles))
                : new SevenZFile(MultiReadOnlySeekableByteChannel.forFiles(srcFiles), password.toCharArray())
        ) {
            List<File> resultFiles = operate(sevenZFile, outputDirectory);
            if (deleteSrc) {
                for (File file : srcFiles) {
                    FileUtil.deleteFile(file);
                }
            }
            return resultFiles;
        }
        catch (JingException e) {
            throw e;
        }
        catch (Throwable t) {
            throw new JingException(t);
        }
    }

    private List<File> operate(SevenZFile sevenZFile, String outputDirectory) throws Throwable {
        List<File> resultFiles = new ArrayList<>();
        if (!FileUtil.mkdirs(outputDirectory)) {
            throw new JingException("failed to make output directory: {}", outputDirectory);
        }
        SevenZArchiveEntry entry;
        File subFile;
        while (null != (entry = sevenZFile.getNextEntry())) {
            if (logFlag) {
                LOGGER.debug("decompressMultiple: {}", entry.getName());
            }
            if (entry.isDirectory()) {
                if (!FileUtil.mkdirs(subFile = new File(outputDirectory + File.separator + entry.getName()))) {
                    throw new JingException("failed to make sub directory: {}", outputDirectory + File.separator + entry.getName());
                }
                resultFiles.add(subFile);
            }
            else {
                try (
                    FileOutputStream writer = new FileOutputStream(subFile = new File(outputDirectory + File.separator + entry.getName()))
                ) {
                    int len;
                    byte[] buffer = new byte[bufferSize];
                    while((len = sevenZFile.read(buffer)) != -1){
                        writer.write(buffer, 0, len);
                    }
                    writer.flush();
                    resultFiles.add(subFile);
                }
            }
        }
        return resultFiles;
    }
}
